﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Web.UI.WebControls;
using Pampa.CRUD.Configuration;
using Pampa.CRUD.Core;
using Pampa.MVC.Fluent.Common;
using Pampa.MVC.Fluent.Crud.Items;

namespace Pampa.MVC.Fluent.Crud.Groups
{
    public class GroupBuilder<TGroup, TEditor> : AbstractBuilder
    {
        protected readonly HashSet<string> _properties;
        protected readonly Group _group;
        protected readonly EntityConfig<TEditor> _entityConfig;

        public GroupBuilder(EntityConfig<TEditor> entityConfig, Group group)
        {
            _properties = new HashSet<string>();
            _entityConfig = entityConfig;
            _group = group;
        }

        public CheckBoxItemBuilder<TGroup, TEditor> AddCheckBox(Expression<Func<TGroup, object>> property)
        {
            return AddCheckBox(GetPropertyId(property));
        }

        public CheckBoxItemBuilder<TGroup, TEditor> AddCheckBox(string propertyId)
        {
            var item = new CheckBoxItem();
            item.Id = propertyId;
            _group.Items.Add(item);

            return new CheckBoxItemBuilder<TGroup, TEditor>(this, item);
        }

        public DateCombosItemBuilder<TGroup, TEditor> AddDateDropDowns(Expression<Func<TGroup, object>> property)
        {
            return AddDateDropDowns(GetPropertyId(property));
        }

        public DateCombosItemBuilder<TGroup, TEditor> AddDateDropDowns(string propertyId)
        {
            var item = new DateCombosItem();
            item.Id = propertyId;
            _group.Items.Add(item);

            return new DateCombosItemBuilder<TGroup, TEditor>(this, item);
        }

        public DateItemBuilder<TGroup, TEditor> AddDatePicker(Expression<Func<TGroup, object>> property)
        {
            return AddDatePicker(GetPropertyId(property));
        }

        public DateItemBuilder<TGroup, TEditor> AddDatePicker(string propertyId)
        {
            var item = new DateItem();
            item.Id = propertyId;
            _group.Items.Add(item);

            return new DateItemBuilder<TGroup, TEditor>(this, item);
        }

        public StaticComboItemBuilder<TGroup, TEditor> AddDropDown(Expression<Func<TGroup, object>> property)
        {
            return AddDropDown(GetPropertyId(property));
        }

        public StaticComboItemBuilder<TGroup, TEditor> AddDropDown(string propertyId)
        {
            var item = new StaticComboItem();
            item.Id = propertyId;
            item.RadioButtons = false;

            _group.Items.Add(item);

            return new StaticComboItemBuilder<TGroup, TEditor>(this, item);
        }

        public EntityComboItemBuilder<TGroup, TEditor, TEntity> AddEntityDropDown<TEntity>(Expression<Func<TGroup, TEntity>> property)
        {
            return AddEntityDropDown<TEntity>(GetPropertyId(property));
        }

        public EntityComboItemBuilder<TGroup, TEditor, TEntity> AddEntityDropDown<TEntity>(string propertyId)
        {
            var item = new EntityComboItem();
            item.Id = propertyId;
            item.EntityName = typeof(TEntity).AssemblyQualifiedName;
            _group.Items.Add(item);

            return new EntityComboItemBuilder<TGroup, TEditor, TEntity>(this, item);
        }

        public SelectorItemBuilder<TGroup, TEditor> AddSelector(Expression<Func<TGroup, object>> property)
        {
            return AddSelector(GetPropertyId(property));
        }

        public SelectorItemBuilder<TGroup, TEditor> AddSelector(string propertyId)
        {
            var item = new SelectorItem();
            item.Id = propertyId;
            item.Multiple = false;
            item.FreeText = true;
            _group.Items.Add(item);

            return new SelectorItemBuilder<TGroup, TEditor>(this, item);
        }

        public EntitySelectorItemBuilder<TGroup, TEditor, TEntity> AddEntitySelector<TEntity>(Expression<Func<TGroup, TEntity>> property)
        {
            return AddEntitySelector<TEntity>(GetPropertyId(property));
        }

        public EntitySelectorItemBuilder<TGroup, TEditor, TEntity> AddEntitySelector<TEntity>(string propertyId)
        {
            var item = new EntitySelectorItem();
            item.Id = propertyId;
            item.EntityName = typeof(TEntity).AssemblyQualifiedName;
            _group.Items.Add(item);

            return new EntitySelectorItemBuilder<TGroup, TEditor, TEntity>(this, item);
        }

        public TextItemBuilder<TGroup, TEditor> AddTextItem(Expression<Func<TGroup, object>> property)
        {
            return AddTextItem(GetPropertyId(property));
        }

        public TextItemBuilder<TGroup, TEditor> AddTextItem(string propertyId)
        {
            var item = new TextItem();
            item.Id = propertyId;
            _group.Items.Add(item);

            return new TextItemBuilder<TGroup, TEditor>(this, item);
        }

        public ViewItemBuilder<TGroup, TEditor> AddViewItem(Expression<Func<TGroup, object>> property)
        {
            return AddViewItem(GetPropertyId(property));
        }

        public ViewItemBuilder<TGroup, TEditor> AddViewItem(string propertyId)
        {
            var item = new ViewItem();
            item.Id = propertyId;
            _group.Items.Add(item);

            return new ViewItemBuilder<TGroup, TEditor>(this, item);
        }

        internal EntityConfig<TEditor> ParentEditor
        {
            get { return _entityConfig; }
        }

        internal string GetGroupId()
        {
            return _group.Id;
        }

        protected string GetPropertyId<TProperty>(Expression<Func<TGroup, TProperty>> action)
        {
            var id = GetId(action);

            if (_properties.Contains(id))
            {
                throw new Exception(string.Format("Property {0} is already defined in group {1}.", id, _group.Id));
            }
            else
            {
                _properties.Add(id);
            }

            return id;
        }
    }
}
